package org.scala.javafx.rich

import java.lang.reflect.Method
import javafx.beans.value.ObservableValue
import javafx.collections.ObservableList
import javafx.collections.ObservableMap
import org.scala.javafx.rich.Enrich._
import scala.collection.JavaConverters._

case class RichClass(underlying: Class[_]) {
  def name = underlying.getName()
  val info = RichClass.info(underlying)
  def fields = info.fields
  def properties = info.properties
  def observableLists = info.observableLists
  def methods = info.methods
  def getters = info.getters
  def setters = info.setters
}

object RichClass {
  case class Info(underlying: Class[_]) {
    lazy val fields = underlying.getFields().map { field => field.name -> field }.toMap
    lazy val methods = underlying.getMethods()
    lazy val properties = methods.flatMap { method =>
      if (classOf[ObservableValue[_]].isAssignableFrom(method.returnType)) {
        Some(method)
      } else {
        None
      }
    }
    lazy val observableLists = methods.flatMap { method =>
      if (classOf[ObservableList[_]].isAssignableFrom(method.returnType)) {
        Some(method)
      } else {
        None
      }
    }
    lazy val getters: Map[String, Method] = methods.flatMap { method =>
      method.getterName.map { _ -> method }
    }.toMap
    lazy val setters: Map[String, Method] = methods.flatMap { method =>
      method.setterName.map { _ -> method }
    }.toMap
  }
  
  private var _cache = Map[Class[_], Info]()
  
  def info(underlying: Class[_]): Info = this.synchronized {
    _cache.get(underlying) match {
      case Some(info) => info
      case None => {
        val info = Info(underlying)
        _cache += (underlying -> info)
        info
      }
    }
  }
  
  def main(args: Array[String]): Unit = {
    import javafx.scene.control.TabPane
    println(new TabPane()._class.getters)
    println(new TabPane()._class.setters)
    println(new TabPane()._class.fields)
  }
}
